from Bases import BaseObject, setID
from category import Category
from masterclock import Clock
from controller import controllerTypes
from sequencer import Sequencer

class EffectsSystem(BaseObject):
    _saved_class_name = 'Effects'
    #_saved_child_objects = ['EffectPalettes', 'Faders']
    def __init__(self, **kwargs):
        self.saved_child_classes = [EffectPalette]
        self.root_category = Category(name='effects_root', id='effects_root')
        self.DeviceSystem = kwargs.get('DeviceSystem')
        super(EffectsSystem, self).__init__(**kwargs)
        self.clock = kwargs.get('clock')
        if not self.clock:
            self.clock = Clock()
        if 'deserialize' not in kwargs:
            self.Palettes = {}
            self.EffectPalettes = {}
            self.AppliedPalettes = {}
            p = self.add_palette(id='Fade', name='Fade', all_groups=True)
            e = p.add_effect(controller_type='Keyframe', name='Fade', id='Fade')
            #e.controller_prototype.add_keyframes({0.0:0.0, 1.0:1.0})
            self.Faders = {}
            p = self.add_palette(id='Shuttle', name='Shuttle')
            e = p.add_effect(controller_type='Functional', name='Shuttle', id='Functional')
            self.Shuttles = {}
        self.Sequencers = {}
        
    def add_palette(self, **kwargs):
        '''
        :Parameters:
            id :
            name :
            
        '''
        kwargs.setdefault('root_category', self.root_category)
        p = EffectPalette(**kwargs)
        self.EffectPalettes.update({p.id:p})
        return p
        
    def add_fader(self, **kwargs):
        '''
        :Parameters:
            id :
            name :
            duration :
        '''
        kwargs.update({'palette_id':'Fade', 'loop_mode':'single', 'auto_start':False})
        f = self.apply_palette(**kwargs)
        self.Faders.update({f.id:f})
        return f
        
    def edit_fader(self, **kwargs):
        '''
        :Parameters:
            fader :
            fader_id :
            objects : {'objid':{'obj':obj, 'start':startval, 'end':endval}}
        '''
        fader_id = kwargs.get('fader_id')
        fader = kwargs.get('fader')
        objects = kwargs.get('objects')
        if not fader and fader_id is not None:
            fader = self.Faders[fader_id]
        e = fader.effects['Fade']
        for key, val in objects.iteritems():
            if key not in fader.obj_ids:
                fader.add_obj(val['obj'])
            c = e.controllers[key]['Dimmer']
            start = val['start'] / c.obj_scale_factor
            end = val['end'] / c.obj_scale_factor
            c.add_keyframes({0.0:start, 1.0:end})
            
    def del_fader(self, **kwargs):
        id = kwargs.get('id')
        fader = kwargs.get('fader')
        if not id and fader is not None:
            id = fader.id
        if not fader:
            fader = self.Faders[id]
        self.clear_palette(palette=fader)
        del self.Faders[id]
        
    def add_shuttle(self, **kwargs):
        '''
        :Parameters:
            id :
            name :
            duration :
            group_names :
        '''
        kwargs.update({'palette_id':'Shuttle', 'loop_mode':'up', 'auto_start':False})
        s = self.apply_palette(**kwargs)
        for name in kwargs.get('group_names', []):
            s.add_group_name(name)
        self.Shuttles.update({s.id:s})
        return s
    
    def add_sequencer(self, **kwargs):
        '''
        :Parameters:
            id :
            name :
            duration : float, duration in seconds
            mode : str, loop mode ('single', 'updn', 'up', 'dn')
        '''
        kwargs.setdefault('root_category', self.root_category)
        kwargs.setdefault('clock', self.clock)
        sq = Sequencer(**kwargs)
        sq.connect('state_changed', self.on_sequencer_state_changed)
        self.Sequencers.update({sq.id:sq})
        return sq
        
    def apply_palette(self, **kwargs):
        '''
        :Parameters:
            id :
            name :
            palette_id :
            palette :
            loop_mode :
            duration :
            objects : list of groups or attributes
        '''
        id = kwargs.get('id')
        palette_id = kwargs.get('palette_id')
        palette = kwargs.get('palette')
        mode = kwargs.get('loop_mode', 'updn')
        objects = kwargs.get('objects', [])
        auto_start = kwargs.get('auto_start', True)
        new_kwargs = kwargs.copy()
        for key in ['palette_id', 'palette', 'loop_mode', 'objects']:
            if key in new_kwargs:
                del new_kwargs[key]
        if not palette and palette_id is not None:
            palette = self.EffectPalettes.get(palette_id)
        if palette:
            duration = kwargs.get('duration', palette.default_duration)
            sq = self.add_sequencer(duration=duration, mode=mode)
            p_kwargs = palette._get_saved_attr()['attrs'].copy()
            p_kwargs['obj_ids'].clear()
            p_kwargs.update({'sequencer':sq, 'prototype':palette})
            p_kwargs.update(new_kwargs)
            new_p = EffectPalette(**p_kwargs)
            palette.connect('settings_changed', new_p.on_settings_changed)
            for obj in objects:
                new_p.add_obj(obj)
            if auto_start:
                sq.start()
            self.AppliedPalettes.update({new_p.id:new_p})
        return new_p
        
    def clear_palette(self, **kwargs):
        id = kwargs.get('id')
        palette = kwargs.get('palette')
        if not palette and id is not None:
            palette = self.AppliedPalettes.get(id)
        if palette:
            palette.unlink()
            #palette.sequencer.stop()
            if palette.id in self.AppliedPalettes:
                del self.AppliedPalettes[palette.id]
    def load_globals(self, **kwargs):
        pass
    def save_globals(self, **kwargs):
        return {}
        
    @property
    def fps(self):
        return self.clock.fps
        
    def on_sequencer_state_changed(self, **kwargs):
        obj = kwargs.get('obj')
        state = kwargs.get('state')
        if not state:
            if obj.id in self.Sequencers and not obj.persistent and len(obj.controllers) == 0:
                del self.Sequencers[obj.id]

class EffectPalette(BaseObject):
    _saved_class_name = 'EffectPalette'
    _saved_attributes = ['id', 'name', 'all_groups', 'group_names', 'obj_ids', 'default_duration', 'prototype_id']
    #_saved_child_objects = ['effects']
    def __init__(self, **kwargs):
        self.saved_child_classes = [Effect]
        super(EffectPalette, self).__init__(**kwargs)
        self.register_signal('settings_changed')
        if 'deserialize' not in kwargs:
            self.id = setID(kwargs.get('id'))
            self.name = kwargs.get('name', '')
            self.default_duration = kwargs.get('duration', 2)
            self.all_groups = kwargs.get('all_groups', False)
            self.group_names = set()
            self.obj_ids = set()
            self.effects = {}
        self._sequencer = kwargs.get('sequencer')
        self.group_obj = {}
        groups = kwargs.get('group_names', [])
        for g in groups:
            self.add_group_name(g)
        
        ptype = kwargs.get('prototype')
        if ptype is not None:
            self.prototype_id = ptype.id
            for key, val in ptype.effects.iteritems():
                d = val._get_saved_attr()['attrs'].copy()
                d.update({'effect_prototype':val})
                d.update({'controller_prototype':val.controller_prototype})
                e = self.add_effect(**d)
                #val.connect('settings_changed', e.on_settings_changed)
        else:
            self.prototype_id = None
        self.prototype = ptype
            
    def unlink(self):
        self.prototype.disconnect(callback=self.on_settings_changed)
#        for key, val in self.prototype.effects.iteritems():
#            e = self.effects.get(key)
#            if e:
#                val.disconnect(callback=e.on_settings_changed)
        for e in self.effects.itervalues():
            e.unlink()
    
    def add_group_name(self, group):
        self.group_names.add(group)
        for e in self.effects.itervalues():
            e.add_group_name(group)
    
    def add_obj(self, obj):
        self.group_obj.update({obj.id:obj})
        self.obj_ids.add(obj.id)
        for e in self.effects.itervalues():
            e.add_obj(obj)
        
    def del_obj(self, key):
        for e in self.effects.itervalues():
            e.del_obj(key)
        if key in self.group_obj:
            del self.group_obj[key]
        self.obj_ids.discard(key)
        
    @property
    def sequencer(self):
        return self._sequencer
    @sequencer.setter
    def sequencer(self, value):
        self._sequencer = value
        for e in self.effects.itervalues():
            e.sequencer = value
            
    def add_effect(self, **kwargs):
        '''
        :Parameters:
            id :
            name :
            controller_type : str, ('Functional, Keyframe')
            group_names : group names to be affected
        '''
        kwargs.update({'sequencer':self.sequencer, 'all_groups':self.all_groups})
        e = Effect(**kwargs)
        for g in self.group_names:
            e.add_group_name(g)
        for obj in self.group_obj.itervalues():
            e.add_obj(obj)
        self.effects.update({e.id:e})
        self.emit('settings_changed', effect=e)
        return e
        
    def on_settings_changed(self, **kwargs):
        if 'effect' in kwargs:
            e = kwargs.get('effect')
            if e.id not in self.effects:
                d = e._get_saved_attr()['attrs'].copy()
                d.update({'effect_prototype':e})
                d.update({'controller_prototype':e.controller_prototype})
                new_e = self.add_effect(**d)
                #e.connect('settings_changed', new_e.on_settings_changed)
        
#controllerTypes = {'Keyframe':KeyframeController, 'Functional':FunctionalController}

class Effect(BaseObject):
    _saved_class_name = 'Effect'
    _saved_attributes = ['id', 'name', 'controller_type', 'obj_ids']
    #_saved_child_objects = ['controllers', 'controller_prototype']
    def __init__(self, **kwargs):
        self.saved_child_classes = controllerTypes.values()
        super(Effect, self).__init__(**kwargs)
        self.register_signal('settings_changed')
        self.all_groups = kwargs.get('all_groups', False)
        if 'deserialize' not in kwargs:
            self.id = setID(kwargs.get('id'))
            self.name = kwargs.get('name', '')
            self.controller_type = kwargs.get('controller_type', 'Functional')
            self.controllers = {}
            self.obj_ids = set()
            cls = controllerTypes.get(self.controller_type)
            c = kwargs.get('controller_prototype')
            if c is not None:
                c_kwargs = c._get_saved_attr()['attrs'].copy()
                new_c = cls(**c_kwargs)
                c.connect('settings_changed', new_c.on_settings_changed)
            else:
                new_c = cls(name=self.name)
            self.controller_prototype = new_c
            self.controller_supertype = c
            
        self.effect_prototype = kwargs.get('effect_prototype')
        if self.effect_prototype:
            self.effect_prototype.connect('settings_changed', self.on_settings_changed)
        self._sequencer = kwargs.get('sequencer')
        self.group_obj = {}
        self.group_names = set()
        
    def unlink(self):
        if self.effect_prototype is not None:
            self.effect_prototype.disconnect(callback=self.on_settings_changed)
        if self.controller_supertype is not None:
            self.controller_supertype.disconnect(callback=self.controller_prototype.on_settings_changed)
        if self.sequencer:
            for c in self.controllers.itervalues():
                self.sequencer.del_controller(c)
        
    @property
    def sequencer(self):
        return self._sequencer
    @sequencer.setter
    def sequencer(self, value):
        old = self._sequencer
        self._sequencer = value
        if old is None and value is not None:
            for c in self.controllers.itervalues():
                self.sequencer.add_controller(c)
    
    def add_group_name(self, group):
        self.group_names.add(group)
        self.assign_controllers()
        self.emit('settings_changed', group_name=group)
    
    def add_obj(self, obj):
        self.group_obj.update({obj.id:obj})
        self.obj_ids.add(obj.id)
        self.assign_controllers()
        
    def del_obj(self, key):
        if key in self.controllers:
            self.controllers[key].unlink()
            del self.controllers[key]
        if key in self.group_obj:
            del self.group_obj[key]
        self.obj_ids.discard(key)
        
    def store_value(self, **kwargs):
        timeV = kwargs.get('timeV')
        objkey = kwargs.get('objkey')
        value = kwargs.get('value')
        if objkey:
            if self.all_groups:
                objval = self.controllers.get(objkey)
            elif False:
                for grkey, grval in self.controllers.iteritems():
                    objval = grval.get(objkey)
                    if objval:
                        break
            if objval:
                objval.store_value(timeV, value)
            else:
                print 'key not found:', objkey
        else:
            if self.all_groups:
                for c in self.controllers.itervalues():
                    c.store_value(timeV, value)                        
            elif False:
                for grval in self.controllers.itervalues():
                    for c in grval.itervalues():
                        if type(c) == list:
                            for realc in c:
                                realc.store_value(timeV, value)
                        else:
                            c.store_value(timeV, value)
                        
    def set_expression(self, **kwargs):
        objkey = kwargs.get('objkey')
        expression = kwargs.get('expression')
        if objkey:
            objval = self.controllers.get(objkey)
            if objval:
                objval.expression = expression
        else:
            for c in self.controllers.itervalues():
                c.expression = expression
    
    def assign_controllers(self):
        if self.all_groups:
            for objkey, objval in self.group_obj.iteritems():
                base = getattr(objval, 'base_class', None)
                if objkey not in self.controllers:
                    if base == 'Group' or base == 'Attribute':
                        c = self._build_controller(obj=objval)
                        self.controllers.update({objkey:c})
            #print self, 'controllers: ', self.controllers
        else:
            for objkey, objval in self.group_obj.iteritems():
                base = getattr(objval, 'base_class', None)
                if objkey not in self.controllers:
                    if base == 'Group' and objval.name in self.group_names:
                        c = self._build_controller(obj=objval)
                        self.controllers.update({objkey:c})
#        elif False:
#            for grkey in self.group_names:
#                if grkey not in self.controllers:
#                    self.controllers.update({grkey:{}})
#                for objkey, objval in self.group_obj.iteritems():
#                    if objval.saved_class_name == 'DeviceGroup':
#                        if grkey in objval.AttributeGroups and objkey not in self.controllers[grkey]:
#                            c = self._build_controller(obj=objval.AttributeGroups[grkey])
#                            self.controllers[grkey].update({objkey:c})
#                            print 'effect=%s, controller=%s, obj=%s' % (self.name, c, objval)
#                    elif getattr(objval, 'base_class') == 'Group':
#                        if grkey.lower() in objval.saved_class_name.lower() and objkey not in self.controllers[grkey]:
#                            if type(objval.value) == list:
#                                self.controllers[grkey].update({objkey:[]})
#                                for index in range(len(objval.value)):
#                                    c = self._build_controller(obj=objval, index=index)
#                                    self.controllers[grkey][objkey].append(c)
#                                    print 'effect=%s, controller=%s, index=%s, obj=%s' % (self.name, c, index, objval)
#                            else:
#                                c = self._build_controller(obj=objval)
#                                self.controllers[grkey].update({objkey:c})
#                                print 'effect=%s, controller=%s, obj=%s' % (self.name, c, objval)
            
    def _build_controller(self, **kwargs):
        id = kwargs.get('id')
        obj = kwargs.get('obj')
        attr = getattr(obj, 'value_attribute', 'value')
        d = self.controller_prototype._get_saved_attr()['attrs']
        d.update({'src_object':obj, 'src_attr':attr})
        if type(getattr(obj, attr)) == dict:        
            cls = controllerTypes['Multi']
            d['controller_type'] = self.controller_type
        else:
            cls = controllerTypes.get(self.controller_type)
        c = cls(**d)
        if self.sequencer:
            self.sequencer.add_controller(c)
        self.controller_prototype.connect('settings_changed', c.on_settings_changed)
        return c
        
    def on_controller_update(self, **kwargs):
        pass
        
    def on_settings_changed(self, **kwargs):
        if 'group_name' in kwargs:
            self.add_group_name(kwargs.get('group_name'))
            
    def _deserialize_child(self, d):
        pass
        
